`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file MemoryMappedUART.v
	@author Andrew D. Zonenberg
	@brief Memory mapping wrapper around the UART
 */
module MemoryMappedUART(
	clk,
	addr, wr, rd, din, wmask, dout, doutok,
	tx, rx
    );

	////////////////////////////////////////////////////////////////////////////////////////////////
	// IO declarations
	input wire clk;
	
	input wire[31:0] addr;
	input wire wr;
	input wire rd;
	input wire[31:0] din;
	input wire[3:0] wmask;
	output reg[31:0] dout = 0;
	output reg doutok = 0;
	
	input wire rx;
	output wire tx;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Memory-mapping logic
	reg[15:0] clkdiv = 0;
	wire uart_txactive;
	
	wire txfifo_overflow;
	wire txfifo_underflow;
	wire txfifo_empty;
	wire txfifo_full;
	
	wire rxfifo_overflow;
	wire rxfifo_underflow;
	wire rxfifo_empty;
	wire rxfifo_full;
	
	wire uart_txerr;
	wire uart_rxactive;
	
	reg rxfifo_rd = 0;
	
	reg tx_wr = 0;
	reg[7:0] tx_wrdata = 0;
	
	wire[7:0] rxbuf;
	
	reg[31:0] ustat = 0;
	
	always @(posedge clk) begin
	
		//Buffer STAT register
		//Set error flags but don't clear them
		ustat[31:16] <= 16'b0;										//High bits
		ustat[15:13] <= 3'b0;										//0x2000, 0x4000, 0x8000
		ustat[12] <= ustat[12] | rxfifo_overflow;				//0x1000
		ustat[11] <= ustat[11] | rxfifo_underflow;			//0x800
		ustat[10] <= rxfifo_empty;									//0x400
		ustat[9] <= rxfifo_full;									//0x200
		ustat[8] <= uart_rxactive;									//0x100
		ustat[7:6] <= 2'b0;											//0x80, 0x40
		ustat[5] <= ustat[5] | txfifo_overflow;				//0x20
		ustat[4] <= ustat[4] | txfifo_underflow;				//0x10
		ustat[3] <= txfifo_empty;									//0x08
		ustat[2] <= txfifo_full;									//0x04
		ustat[1] <= uart_txactive;									//0x02
		ustat[0] <= uart_txerr;										//0x01
	
		tx_wr <= 0;
		tx_wrdata <= 0;
		
		rxfifo_rd <= 0;
		
		dout <= 0;
		doutok <= 0;
	
		//Writing
		if(wr) begin
	
			//TXBUF
			if(addr[7:4] == 0 && wmask[0]) begin
				tx_wrdata <= din[7:0];
				tx_wr <= 1;
			end
			
			//RXBUF cannot be written to
			
			//BRG
			if(addr[7:4] == 2) begin
				if(wmask[0])
					clkdiv[7:0] <= din[7:0];
				if(wmask[1])
					clkdiv[15:8] <= din[15:8];
			end
			
			//STAT
			//Only certain bits can be written to, and only changed to zeros (to clear error flags)
			if(addr[7:4] == 3) begin
				
				//Base register
				//Only allow writes of zero to error bits
				if(addr[3:2] == 0) begin
					if(wmask[1]) begin
						if(!din[12])
							ustat[12] <= 0;
						if(!din[11])
							ustat[11] <= 0;
					end
					if(wmask[0]) begin
						if(!din[5])
							ustat[5] <= 0;
						if(!din[4])
							ustat[4] <= 0;
						if(!din[0])
							ustat[0] <= 0;
					end
				end
					
				//SET register unimplemented for now as no bits can be set
					
				//CLR register
				if(addr[3:2] == 2) begin
					if(wmask[1]) begin
						if(din[12])
							ustat[12] <= 0;
						if(din[11])
							ustat[11] <= 0;
					end
					if(wmask[0]) begin
						if(din[5])
							ustat[5] <= 0;
						if(din[4])
							ustat[4] <= 0;
						if(din[0])
							ustat[0] <= 0;
					end
				end
					
				//INV register only clears bits for now since none can be set
				if(addr[3:2] == 3) begin
					if(wmask[1]) begin
						if(din[12])
							ustat[12] <= 0;
						if(din[11])
							ustat[11] <= 0;
					end
					if(wmask[0]) begin
						if(din[5])
							ustat[5] <= 0;
						if(din[4])
							ustat[4] <= 0;
						if(din[0])
							ustat[0] <= 0;
					end
				end
			end
			
		end
		
		//Reading
		if(rd) begin
			
			//TXBUF cannot be read
			
			//RXBUF
			if(addr[7:4] == 1) begin
				dout <= {24'h0, rxbuf};
				doutok <= 1;
				
				//Pop the FIFO
				rxfifo_rd <= 1;
			end
			
			//BRG
			if(addr[7:4] == 2) begin
				dout <= {16'h0, clkdiv};
				doutok <= 1;
			end
			
			//STAT
			if(addr[7:4] == 3) begin
				
				dout <= ustat;			
				doutok <= 1;
			end
		
		end
		
	end
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// FIFOs
	
	reg txen = 0;
	wire[7:0] txd;
	wire[7:0] txd_r;	//not used
	SmallLutramFifo #(.WIDTH(8)) txfifo (
		.clk(clk), 
		.wr(tx_wr), 
		.din(tx_wrdata), 
		.rd(txen), 
		.dout_c(txd), 
		//.dout_r(txd_r), 
		.overflow(txfifo_overflow), 
		.underflow(txfifo_underflow), 
		.empty(txfifo_empty), 
		.full(txfifo_full)
		);
	
	wire rx_wr;
	wire[7:0] rx_din;
	wire[7:0] rxbuf_r;	//not used
	SmallLutramFifo #(.WIDTH(8)) rxfifo (
		.clk(clk), 
		.wr(rx_wr), 
		.din(rx_din), 
		.rd(rxfifo_rd),
		.dout_c(rxbuf), 
		//.dout_r(rxbuf_r), 
		.overflow(rxfifo_overflow), 
		.underflow(rxfifo_underflow), 
		.empty(rxfifo_empty), 
		.full(rxfifo_full)
		);
		
	////////////////////////////////////////////////////////////////////////////////////////////////
	// FIFO push/pop logic
	
	always @(posedge clk) begin
		txen <= 0;
		
		//Don't pop twice in a row or we might underflow!
		if(!txfifo_empty && !txen && !uart_txactive) begin
			txen <= 1;
		end
	end

	////////////////////////////////////////////////////////////////////////////////////////////////
	// The UART

	UART uart (
		.clk(clk), 
		.clkdiv(clkdiv), 
		.tx(tx), 
		.txin(txd), 
		.txrdy(txen), 
		.txactive(uart_txactive), 
		.rx(rx), 
		.rxout(rx_din), 
		.rxrdy(rx_wr), 
		.rxactive(uart_rxactive), 
		.overflow(uart_txerr)
		);

endmodule
